package auditoryUIbk;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;

import auditoryTree.TreeModel;
import auditoryUIbk.JExperimentProgress.JProgress.Cond;
import experiment.*;

public class JExperimentProgress extends JWindow {
	
	static final boolean SIMPLE_MODE = true; // i.e., only total percentage is shown
	
	public static final Dimension barsize = new Dimension(Toolkit.getDefaultToolkit().getScreenSize().width - (SIMPLE_MODE?200:15),
			SIMPLE_MODE?40:26);
	public static final Dimension winsize = new Dimension(Toolkit.getDefaultToolkit().getScreenSize().width,
			SIMPLE_MODE?40:26);
	
	int x0 = 5;
	int y0 = SIMPLE_MODE?40 : 5;
	
	/**
	 * The progress component
	 */
	static class JProgress extends JComponent {
				
		static class Cond {
			int index;
			int trials;
			int done = 0;
			double trialtime;
			boolean breakAfter = false;
		}

		static Color bg = new Color(0.1f, 0.2f, 0.5f);
		static Color trial0 = new Color(1f, 1f, 1f, 0.1f);
		static Color trial1 = new Color(0.2f, 1f, 0.2f, 0.6f);
		static Font timefont = new Font("Helvetica", Font.BOLD, SIMPLE_MODE?24: 16);
		double trialspace = SIMPLE_MODE?0: 1000; // in ms
		double conditionspace = SIMPLE_MODE?0: 1000 * 20;
		double breakspace = SIMPLE_MODE?0: 1000 * 20;//1000 * 60 * 5;
		double yspace = 5;
		double bar_x0 = SIMPLE_MODE?200: 45; // x just after experiment time
		int text_x0 = SIMPLE_MODE?28:19;
		
		ArrayList conds = new ArrayList();
		double totaltime;
		int currentcondition = 0;
		int currenttrial = 0;
		Rectangle2D.Double rect = new Rectangle2D.Double();
		Line2D.Double line = new Line2D.Double();
		
		public void paint(Graphics g1) {
			Graphics2D g = (Graphics2D)g1;
			g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
			g.setColor(bg);
			g.fillRect(0, 0, winsize.width, winsize.height);
			
			// paint time
			g.setColor(trial1);
			g.setFont(timefont);
			g.drawString(Logger.getTimeSinceExperimentStart(), 4, text_x0);			
			
			// paint progress
			if (totaltime == 0) return;			
			Cond c;
			double time = 0;
			double current_progress_time = 0;
			for (int i=0; i<conds.size(); i++) {
				c = (Cond)conds.get(i);

				if (trialspace == 0) { // no visual separation between trials
					if (i < currentcondition) {
						g.setColor(trial1);
						rect.setFrame(bar_x0 + x(time), yspace, x(c.trials*c.trialtime), barsize.height-2*yspace);
						g.fill(rect);
					} else if (i > currentcondition) {
						g.setColor(trial0);	
						rect.setFrame(bar_x0 + x(time), yspace, x(c.trials*c.trialtime), barsize.height-2*yspace);
						g.fill(rect);
					} else {
						g.setColor(trial1);
						rect.setFrame(bar_x0 + x(time), yspace, x(currenttrial*c.trialtime), barsize.height-2*yspace);
						g.fill(rect);
						g.setColor(trial0);
						rect.setFrame(bar_x0 + x(time+currenttrial*c.trialtime), yspace, x((c.trials-currenttrial)*c.trialtime), barsize.height-2*yspace);
						g.fill(rect);
						current_progress_time = time + currenttrial*c.trialtime;
					}
					time += c.trials*c.trialtime;
				} else {
					if (i <= currentcondition)
						g.setColor(trial1);
					if (i > currentcondition)
						g.setColor(trial0);
					if (i == currentcondition)
						current_progress_time = time + currenttrial*c.trialtime;
					for (int t=0; t<c.trials; t++) {
						if (i == currentcondition && t == currenttrial) {
							g.setColor(trial0);
						}
						rect.setFrame(bar_x0 + x(time), yspace, x(c.trialtime), barsize.height-2*yspace);
						g.fill(rect);
						time += c.trialtime + trialspace;
					}
				}
				time += conditionspace;
				if (!SIMPLE_MODE) {
					if (c.breakAfter) {
						line.setLine(bar_x0 + x(time) -1, 0, bar_x0 + x(time) -1, barsize.height);
						g.draw(line);
						line.setLine(bar_x0 + x(time) +1, 0, bar_x0 + x(time) +1, barsize.height);
						g.draw(line);
						time += breakspace;
					}
				}
			}
			
			// paint percentage
			g.setColor(trial1);
			g.setFont(timefont);
			//double xx = bar_x0 + (size.width-bar_x0)/2 - 50;
			double xx = x(current_progress_time) + 10;
			int percent = (int)Math.round(current_progress_time/totaltime*100);
			g.drawString(percent + " %", (int)Math.round(bar_x0 + xx), text_x0);
		}
		
		private double x(double time) {
			return time/totaltime*(barsize.width-bar_x0);
		}

		// for optimization purposes
		public void repaintTrial(int condition, int trial) {
			Cond c;
			double time = 0;
			for (int i=0; i<condition; i++) {
				c = (Cond)conds.get(i);
				time += c.trials*(c.trialtime + trialspace);
				time += conditionspace;
				if (c.breakAfter)
					time += breakspace;
			}
			c = (Cond)conds.get(condition);
			time += (trial-1)*(c.trialtime + trialspace);
			repaint((int)(bar_x0 + x(time))-1, 0, (int)(x(c.trialtime))+4, barsize.height);
		}
		
		public void repaintTime() {
			repaint(0, 0, (int)bar_x0, barsize.height);
		}
				
		public Dimension getPreferredSize() {
			return winsize;
		}
		
		public void add(Cond c) {
			conds.add(c);
		}
		
		public void finish() {
			Cond c;
			totaltime = 0;
			double mintrialtime = 10000;
			for (int i=0; i<conds.size(); i++) {
				c = (Cond)conds.get(i);
				totaltime += c.trials * (c.trialtime + trialspace);
				totaltime += conditionspace;
				if (c.breakAfter)
					totaltime += breakspace;
				if (c.trialtime < mintrialtime)
					mintrialtime = c.trialtime;
			}
			
			totaltime -= conditionspace;
			
			if (trialspace > 0 && mintrialtime/totaltime*(barsize.width-bar_x0) < 2) {
				// if smallest trial box <= 2 pixels, don't show individual trials
				trialspace = 0;
				finish();
			}
		}
		
		public void setCurrentCondition(int c) {
			boolean found = false;
			int i=0;
			for (; i<conds.size() && !found; i++)
				found = ((Cond)conds.get(i)).index == c;
			currentcondition = i-1;
			currenttrial = 0;
			repaint();
		}

		public void setCurrentTrial(int t) {
			currenttrial = t;
			repaintTrial(currentcondition, t);
		}
		
		public void endOfExperiment() {
			currentcondition = conds.size()-1;
			currenttrial = ((Cond)conds.get(currentcondition)).trials;
			repaint();
		}
		
	}
	

	
	final JProgress progress = new JProgress();
	
	public JExperimentProgress(SimpleStrokeAudioUIbk ui) {
		//
		//generateExperimentSummary(ui);
		
//		setAlwaysOnTop(true);
		addExperimentData(progress, ui);
		getContentPane().add(progress);
		pack();
		setLocation(x0, y0);
		setBackground(JProgress.bg);
		// repaint timer, for updating time every minute
		Timer timer = new Timer(1000 /600, new ActionListener () {
			public void actionPerformed(ActionEvent e) {
				progress.repaintTime();
			}
		});
		timer.start();
	}
	
	/**
	 * 
	 */
	public void addExperimentData(JProgress comp, SimpleStrokeAudioUIbk ui) {
		int cn = 0;
        for (Iterator it=ui.moConditionList.iterator(); it.hasNext(); ) {
        	cn++;
        	ExperimentCondition condition = (ExperimentCondition)it.next();
            ArrayList stimuli = (ArrayList)ui.moExperimentSet.get(condition.toString());
            if (stimuli != null && stimuli.size() > 0) {
            	Cond c = new Cond();
            	c.index = cn-1;
            	c.trials = stimuli.size();
            	c.trialtime = condition.estimateTimePerTrial();
	            // add break ?
	            if (cn < ui.moConditionList.size() && condition.breakAfter(ui.miParticipantID)) {
	            	c.breakAfter = true;
	            }
	            comp.add(c);
            }
        }
		comp.finish();
	}

	/**
	 * For std output only
	 */
	public void generateExperimentSummary(SimpleStrokeAudioUIbk ui) {
		
		int cn = 0, totaltime=0;
		System.out.println("USER " + ui.miParticipantID);
        for (Iterator it=ui.moConditionList.iterator(); it.hasNext(); ) {
        	cn++;
        	ExperimentCondition condition = (ExperimentCondition)it.next();
        	ArrayList stimuli = (ArrayList)ui.moExperimentSet.get(condition.toString());
            if (stimuli.size() > 0) {
            	System.out.println();
            	System.out.println("CONDITION " + cn + ": " + condition.toString());
            	double trialsPerRepeats = stimuli.size()/condition.getRepeatsPerBlock(ui.miParticipantID)/condition.getNumberOfBlocks(ui.miParticipantID);
	            System.out.println("  " + stimuli.size() + " stimuli (" +
	            		trialsPerRepeats + " trials x " +
	            		condition.getRepeatsPerBlock(ui.miParticipantID) + " repeats x " +
	            		condition.getNumberOfBlocks(ui.miParticipantID) + " blocks)");
	            double time = condition.estimateTimePerTrial() * stimuli.size();
	            // add 6% errors
	            time += time * 6 / 100;
	            // add 1mn for preparatory stuff, "condition start", "condition end", etc.
	            time += 1000*60;
	            System.out.println("  Estimated time: " + msToString(time));
	            // display ordering
	            System.out.println("  Content: ");
	            String stim_num, stim_names;
	            String[] stims;
	            // force ui to reorder the menu
	            ui.copyAndFilterMenuData(condition.getItemsPreShuffling(ui.miParticipantID), condition.getCategorySlice(ui.miParticipantID)[0], condition.getCategorySlice(ui.miParticipantID)[1], condition.getItemsPostShuffling(ui.miParticipantID));
	            TreeModel tree = ui.moList;
	            for (int i = 0; i < tree.childCount(); i++) {
	            	TreeModel category = tree.getChild(i);
	            	if (condition.miDepth == 1) {
	            		stim_num = i + "";
	            		stim_names = category.getTitle();
            			System.out.println("    " + stim_num + " " + stim_names);
	            	} else {
	            		System.out.print("    ");
	            		for (int j = 0; j < condition.miBreadth; j++) {
	            			TreeModel child = category.getChild(j);
	            			stim_num = i + "_" + j;
	            			stim_names = category.getTitle() + " " + child.getTitle();
	            			System.out.print(stim_num + " " + stim_names + " ");
	            		}
	            		System.out.println();
	            	}
	            }
	            // display stimuli
	            System.out.print("  Trials: ");
	            int lastblock = -1;
	            for (Iterator it2=stimuli.iterator(); it2.hasNext(); ) {
	            	Stimulus s = (Stimulus)it2.next();
	            	if (s.getBlockNumber() != lastblock) {
	            		System.out.println();
	            		System.out.print("    ");
	            		lastblock = s.getBlockNumber();
	            	}
	            	for (int i=0; i<s.getStimuli().length; i++) {
	            		System.out.print(s.getStimuli()[i]);
	            		if (i < s.getStimuli().length-1)
	            			System.out.print("_");
	            	}
	            	System.out.print(" ");
	            }
	            System.out.println();
	            // Add to total time
	            totaltime += time;
	            // add 5 more minutes if the condition ends with a break
	            if (cn < ui.moConditionList.size() && condition.breakAfter(ui.miParticipantID)) {
	            	System.out.println();
	            	System.out.println("BREAK (5mn)");
	            	totaltime += 1000*60*5;
	            }
            }
        }
		System.out.println();
		System.out.println("==> Estimated total time: " + msToString(totaltime));
		System.out.println();
	}
	
	private static String msToString(double time) {
		if (time < 60*1000)
			return (int)Math.round(time/1000) + "s";
		if (time < 60*1000*60)
			return (int)Math.round(time/60/1000) + "mn";
		int hours = (int)time/60/1000/60;
		return hours + "h " + msToString(time - hours*60*1000*60); 
	}
	
	public void setCurrentCondition(int c) {
		progress.setCurrentCondition(c);
	}

	public void setCurrentTrial(int t) {
		progress.setCurrentTrial(t);
	}
	
	public void endOfExperiment() {
		progress.endOfExperiment();
	}
}
